﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace GestaltDesigner
{
    public partial class DesignSurface : UserControl
    {
        private SelectionUI _selectionUI;
        private FrameworkElement _selectedElement;
        private bool _isMouseDown;
        private Point _startingPoint;
        private long LastTicks = 0;
        public PropertyGridDataObject propertyGridDataObject;

        public static readonly DependencyProperty OwningControl = DependencyProperty.Register("OwningControl", typeof(FrameworkElement), typeof(UIElement), null);
        public static readonly DependencyProperty OwnedShadow = DependencyProperty.Register("OwnedShadow", typeof(UIElement), typeof(UIElement), null);

        public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
        public event EventHandler DefaultEventAdded;

        public DesignSurface()
        {
            InitializeComponent();
            _selectionUI = new SelectionUI();
            _selectionUI.Opacity = 0;
            this._adornerLayer.Children.Add(_selectionUI);
            this._adornerLayer.MouseLeftButtonUp += new MouseButtonEventHandler(adornerLayer_MouseLeftButtonUp);
            this._selectionUI.MouseLeftButtonDown += new MouseButtonEventHandler(selectionUI_MouseLeftButtonDown);
            this._selectionUI.MouseMove += new MouseEventHandler(selectionUI_MouseMove);
            this._selectionUI.MouseLeftButtonUp += new MouseButtonEventHandler(selectionUI_MouseLeftButtonUp);
            this._selectionUI.SizeOrPositionChanged += new EventHandler(_selectionUI_SizeOrPositionChanged);
            this.designSurfaceTab.IsChecked = true;
        }
        void _selectionUI_SizeOrPositionChanged(object sender, EventArgs e)
        {
            // Size & position element
            _selectedElement.Height = _selectionUI.Height - 16;
            _selectedElement.Width = _selectionUI.Width - 16;
            _selectedElement.SetValue(Canvas.LeftProperty, (double)_selectionUI.GetValue(Canvas.LeftProperty) + 8);
            _selectedElement.SetValue(Canvas.TopProperty, (double)_selectionUI.GetValue(Canvas.TopProperty) + 8);
            //update property grid
            propertyGridDataObject = (PropertyGridDataObject)this.DataContext;
            propertyGridDataObject.Height = _selectedElement.Height;
            propertyGridDataObject.Width = _selectedElement.Width;
            (_selectedElement.GetValue(DesignSurface.OwnedShadow) as FrameworkElement).Height = _selectionUI.Height - 16;
            (_selectedElement.GetValue(DesignSurface.OwnedShadow) as FrameworkElement).Width = _selectionUI.Width - 16;
            (_selectedElement.GetValue(DesignSurface.OwnedShadow) as FrameworkElement).SetValue(Canvas.LeftProperty, (double)_selectionUI.GetValue(Canvas.LeftProperty) + 8);
            (_selectedElement.GetValue(DesignSurface.OwnedShadow) as FrameworkElement).SetValue(Canvas.TopProperty, (double)_selectionUI.GetValue(Canvas.TopProperty) + 8);
        }

        public void UpdateSelectionUI()
        {
            // Size & position shadow
            if (this.SelectedElement != null)
            {
                _selectionUI.Height = this.SelectedElement.Height + 16;
                _selectionUI.Width = this.SelectedElement.Width + 16;
                _selectionUI.SetValue(Canvas.LeftProperty, ((double)this.SelectedElement.GetValue(Canvas.LeftProperty)) - 8);
                _selectionUI.SetValue(Canvas.TopProperty, ((double)this.SelectedElement.GetValue(Canvas.TopProperty)) - 8);
                _selectionUI.Opacity = 1;
            }
        }

        public FrameworkElement SelectedElement
        {
            get
            {
                return _selectedElement;
            }

            set
            {
                if (value != null)
                {
                    _selectionUI.Height = value.Height + 16;
                    _selectionUI.Width = value.Width + 16;
                    _selectionUI.SetValue(Canvas.LeftProperty, ((double)value.GetValue(Canvas.LeftProperty)) - 8);
                    _selectionUI.SetValue(Canvas.TopProperty, ((double)value.GetValue(Canvas.TopProperty)) - 8);
                    _selectionUI.Opacity = 1;
                }
                else
                {
                    _selectionUI.Opacity = 0;
                    
                }

                _selectedElement = value;
                SelectionChangedEventArgs args = new SelectionChangedEventArgs(value, _selectedElement);

                if (SelectionChanged != null)
                {
                    SelectionChanged(this, args);
                }
            }
        }

        public XamlEditor XamlEditor
        {
            get
            {
                return this.xamlSurface;
            }
        }

        public void AddControl(FrameworkElement control)
        {
            this._designSurfaceRoot.Children.Add(control);
            Button b = this._designSurfaceRoot.FindName(control.Name) as Button;
            this._elementShadowLayer.Children.Add(GenerateShadowElement(control));
            this.SelectedElement = control;
            //catch image failed and media failed to prevent design surface from throwing exception
            if (control is Image)
                ((Image)control).ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(DesignSurface_MediaOrimageFailed);
            if (control is MediaElement)
                ((MediaElement)control).MediaFailed += new EventHandler<ExceptionRoutedEventArgs>(DesignSurface_MediaOrimageFailed);
            if (control is MultiScaleImage)
                ((MultiScaleImage)control).ImageOpenFailed += new EventHandler<ExceptionRoutedEventArgs>(DesignSurface_MediaOrimageFailed);
        }



        void DesignSurface_MediaOrimageFailed(object sender, ExceptionRoutedEventArgs e)
        {
            //swallow for now
        }


        public void RemoveControl(FrameworkElement control)
        {
            _elementShadowLayer.Children.Remove(this.SelectedElement.GetValue(DesignSurface.OwnedShadow) as UIElement);
            _designSurfaceRoot.Children.Remove(this.SelectedElement);
            //clean up event handlers
            if (control is Image)
                ((Image)control).ImageFailed -=  DesignSurface_MediaOrimageFailed;
            if (control is MediaElement)
                ((MediaElement)control).MediaFailed -= DesignSurface_MediaOrimageFailed;
            if (control is MultiScaleImage)
                ((MultiScaleImage)control).ImageOpenFailed -= DesignSurface_MediaOrimageFailed;
        }

        UIElement GenerateShadowElement(FrameworkElement control)
        {
            Rectangle rect = new Rectangle();
            rect.SetValue(Canvas.TopProperty, control.GetValue(Canvas.TopProperty));
            rect.SetValue(Canvas.LeftProperty, control.GetValue(Canvas.TopProperty));
            rect.Width = control.Width;
            rect.Height = control.Height;
            rect.Fill = new SolidColorBrush(Color.FromArgb(0x01, 0, 0, 0));
            rect.SetValue(DesignSurface.OwningControl, control);
            control.SetValue(DesignSurface.OwnedShadow, rect);
            return rect;
        }

        void selectionUI_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _isMouseDown = true;
            Point mousePoint = e.GetPosition(_designSurfaceRoot);
            _startingPoint = new Point(mousePoint.X - (double)_selectionUI.GetValue(Canvas.LeftProperty), mousePoint.Y - (double)_selectionUI.GetValue(Canvas.TopProperty));
            _selectionUI.CaptureMouse();
        }

        void selectionUI_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isMouseDown)
            {
                // move element & selection UI
                _selectionUI.SetValue(Canvas.LeftProperty, e.GetPosition(_designSurfaceRoot).X - _startingPoint.X);
                _selectionUI.SetValue(Canvas.TopProperty, e.GetPosition(_designSurfaceRoot).Y - _startingPoint.Y);
                _selectedElement.SetValue(Canvas.LeftProperty, e.GetPosition(_designSurfaceRoot).X - _startingPoint.X + 8);
                _selectedElement.SetValue(Canvas.TopProperty, e.GetPosition(_designSurfaceRoot).Y - _startingPoint.Y + 8);
                (_selectedElement.GetValue(DesignSurface.OwnedShadow) as FrameworkElement).SetValue(Canvas.LeftProperty, e.GetPosition(_designSurfaceRoot).X - _startingPoint.X + 8);
                (_selectedElement.GetValue(DesignSurface.OwnedShadow) as FrameworkElement).SetValue(Canvas.TopProperty, e.GetPosition(_designSurfaceRoot).Y - _startingPoint.Y + 8);
            }
        }

        void selectionUI_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _isMouseDown = false;
            _selectionUI.ReleaseMouseCapture();
        }

        void adornerLayer_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {

            IEnumerable<UIElement> hitTestResults = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(App.Current.RootVisual), App.Current.RootVisual);

            foreach (UIElement uie in hitTestResults)
            {
                Rectangle rect = uie as Rectangle;
                if (rect != null && rect.Parent == _elementShadowLayer)
                {
                    // Double click test
                    if ((DateTime.Now.Ticks - LastTicks) < 3310000)
                    {
                        if (DefaultEventAdded != null)
                        {
                            DefaultEventAdded(rect.GetValue(DesignSurface.OwningControl) as FrameworkElement, new EventArgs());
                        }
                    }
                    else 
                    {
                        this.SelectedElement = rect.GetValue(DesignSurface.OwningControl) as FrameworkElement;
                    }
                    break;
                }
            }

            LastTicks = DateTime.Now.Ticks;
        }


        public void ShowXamlTab()
        {
            this.previewPanel.Visibility = Visibility.Collapsed;
            this.designSurface.Visibility = Visibility.Collapsed;
            this.xamlSurface.Visibility = Visibility.Visible;
            this.designSurfaceTab.IsChecked = false;
            this.previewTab.IsChecked = false;
        }

        public void ShowDesignSurfaceTab()
        {
            this.previewPanel.Visibility = Visibility.Collapsed;
            this.designSurface.Visibility = Visibility.Visible;
            this.xamlSurface.Visibility = Visibility.Collapsed;
            this.xamlSurfaceTab.IsChecked = false;
            this.previewTab.IsChecked = false;
        }

        public void ShowPreviewTab()
        {
            this.previewPanel.Visibility = Visibility.Visible;
            this.designSurface.Visibility = Visibility.Collapsed;
            this.xamlSurface.Visibility = Visibility.Collapsed;
            this.xamlSurfaceTab.IsChecked = false;
            this.designSurfaceTab.IsChecked = false;
        }


        private void xamlSurfaceTabClick(object sender, RoutedEventArgs e)
        {
            this.xamlSurface.SerializeXaml(this._designSurfaceRoot);
            ShowXamlTab();
        }

        private void designSurfaceTabClick(object sender, RoutedEventArgs e)
        {
            ShowDesignSurfaceTab();
        }

        private void previewTabClick(object sender, RoutedEventArgs e)
        {
            this.xamlSurface.SerializeXaml(this._designSurfaceRoot);
            this.previewPanel.GeneratePreview(this.xamlSurface.Xaml);
            ShowPreviewTab();
        }
    }

    public class SelectionChangedEventArgs : EventArgs
    {
        private FrameworkElement _newSelection;
        private FrameworkElement _oldSelection;

        public SelectionChangedEventArgs(FrameworkElement newSelection, FrameworkElement oldSelection)
        {
            _newSelection = newSelection;
            _oldSelection = oldSelection;
        }

        public FrameworkElement NewSelection
        {
            get
            {
                return _newSelection;
            }
        }

        public FrameworkElement OldSelection
        {
            get
            {
                return OldSelection;
            }
        }
    }
}
